<template>
  <div style="text-align:center;padding:50px;">
    <canvas
      id="map"
      ref="map"
      width="600"
      height="600"
      style=""
    />
    <el-button @click="stop">暂停</el-button>
  </div>
</template>
<style>
  #map{
    border:1px solid #dad4d4;
  }
</style>
<script>
export default {
  data: function() {
    return {
      width: 600,
      height: 600,
      squareWidth: 40,
      map: null,
      ctx: null,
      snake: null,
      food: null
    }
  },
  mounted() {
    this.initContext()
    this.initSquareLine()
    this.createSnake()
    this.createFood()
  },
  created() {
    this.registerListener()
  },
  beforeDestroy() {
    if (this.interval) {
      clearInterval(this.interval)
    }
  },
  methods: {
    registerListener: function() {
      const self = this
      document.onkeydown = function() {
        const key = window.event.keyCode
        if (self.snake && key && key >= 37 && key <= 40) {
          self.snake.direction = key
        }
      }
    },
    initContext: function() {
      this.map = this.$refs.map
      this.ctx = this.map.getContext('2d')
    },
    initSquareLine: function() {
      this.ctx.beginPath()
      this.ctx.translate(0.5, 0.5)
      this.ctx.lineWidth = 0.22
      this.ctx.fillStyle = '#ccc'
      // 绘制横线
      for (let i = 1; i < this.height / this.squareWidth; i++) {
        this.ctx.moveTo(0, this.squareWidth * i)
        this.ctx.lineTo(this.width, this.squareWidth * i)
      }
      // 绘制竖线
      for (let x = 1; x < this.width / this.squareWidth; x++) {
        this.ctx.moveTo(this.squareWidth * x, 0)
        this.ctx.lineTo(this.squareWidth * x, this.height)
      }
      this.ctx.stroke()
    },
    createSnake: function() {
      const initX = (this.width / 2) % this.squareWidth === 0 ? (this.width / 2) : (this.width / 2) - ((this.width / 2) % this.squareWidth)
      const initY = (this.height / 2) % this.squareWidth === 0 ? (this.height / 2) : (this.height / 2) - ((this.width / 2) % this.squareWidth)
      const ctx = this.ctx
      const squareWidth = this.squareWidth
      const self = this
      const snake = {
        // 移动方向
        direction: 38,
        // 移动大小
        moveDistance: squareWidth,
        // 移动速度
        deep: 250,
        // 头部
        head: {
          xy: [initX, initY],
          color: '#61cc3c',
          size: squareWidth
        },
        // 移动痕迹
        trace: [],
        interval: '',
        // 身体
        body: {
          size: squareWidth,
          color: '#cc8932',
          xy: [[initX + squareWidth, initY], [initX + squareWidth * 2, initY]]
        },
        move: function() {
          const headTemp = [this.head.xy[0], this.head.xy[1]]
          // 最后一节保存为移动痕迹
          this.addTrace(
            [
              this.body.xy[this.body.xy.length - 1][0],
              this.body.xy[this.body.xy.length - 1][1]
            ]
          )

          // 如果向上移动
          if (this.direction === 38) {
            this.head.xy[1] = this.head.xy[1] - this.moveDistance
            // 如果向左
          } else if (this.direction === 39) {
            this.head.xy[0] = this.head.xy[0] + this.moveDistance
            // 向下
          } else if (this.direction === 40) {
            this.head.xy[1] = this.head.xy[1] + this.moveDistance
            // 向右
          } else if (this.direction === 37) {
            this.head.xy[0] = this.head.xy[0] - this.moveDistance
          }
          // 身体从第二节放到第一节的位置 第三节放到第二节的位置..
          for (let index = this.body.xy.length - 1; index > 0; index--) {
            this.body.xy[index] = this.body.xy[index - 1]
          }
          // 这一句话必须放for循环后面 因为如果当index=0的时候获取的是this.body.xy[0] 然而此时已经变更为蛇头的位置
          // 身体第一节放到之前蛇头的位置
          this.body.xy[0] = headTemp
          // 移动完吃东西
          this.eat(self.food)
          // 从新绘制
          this.show()
        },
        eat: function(food) {
          // 查找当前是否吃到了食物
          if (food && !food.eaten) {
            // 如果吃到了食物 那么身体长大 食物刷新
            if (this.head.xy[0] === food.xy[0] && this.head.xy[1] === food.xy[1]) {
              const by = this.trace[this.trace.length - 1]
              // 新增身体
              this.body.xy[this.body.xy.length] = by
              // 制造食物
              food.make()
            } else {
              // 擦除最后一个的移动痕迹
              this.removeMoveTrace(this.trace[this.trace.length - 1])
            }
          }
        },
        show: function() {
          // 绘制body
          ctx.fillStyle = this.body.color
          for (let index = 0; index < this.body.xy.length; index++) {
            const xy = this.body.xy[index]
            ctx.fillRect(xy[0] + 1, xy[1] + 1, this.body.size - 2, this.body.size - 2)
          }
          ctx.beginPath()
          // 绘制head
          ctx.fillStyle = this.head.color
          ctx.fillRect(this.head.xy[0] + 1, this.head.xy[1] + 1, this.head.size - 2, this.head.size - 2)
          ctx.beginPath()
        },
        // 移除最后的移动痕迹
        removeMoveTrace: function(xy) {
          ctx.beginPath()
          ctx.clearRect(xy[0] + 0.22, xy[1], this.body.size - 1, this.body.size - 1)
        },
        addTrace: function(trace) {
          if (this.trace.length >= 2) {
            this.trace[0] = this.trace[1]
            this.trace[1] = trace
          } else if (this.trace.length < 2) {
            this.trace.push(trace)
          } else {
            alert('移动痕迹错误')
          }
        }
      }
      this.snake = snake
      this.snake.show()
      this.interval = setInterval(function() {
        self.snake.move()
      }, self.snake.deep)
    },
    createFood: function() {
      const self = this
      const squareWidth = this.squareWidth
      const ctx = this.ctx
      this.food = {
        xy: [],
        color: '#e996a9',
        size: squareWidth,
        eaten: false,
        make: function() {
          let x = self.randomNum(0, self.width - squareWidth)
          let y = self.randomNum(0, self.height - squareWidth)
          x = x - x % squareWidth
          y = y - y % squareWidth
          this.xy = [x, y]
          if (x === 0 || x === self.width) {
            this.make()
          } else if (y === 0 || y === self.height) {
            this.make()
          } else if (self.checkInSideBody(self.snake.body.xy, this.xy)) {
            this.make()
          }
          ctx.beginPath()
          ctx.fillStyle = this.color
          const img = new Image()
          const fd = this
          img.onload = function() {
            // 将图片画到canvas上面上去！
            ctx.drawImage(img, fd.xy[0] + 1, fd.xy[1] + 1, self.food.size - 2, self.food.size - 2)
          }

          img.src = 'https://lsh-server.oss-cn-beijing.aliyuncs.com/snake/food/%E6%9C%88%E9%A5%BC.png'
        }
      }
      this.food.make()
    },
    // 生成从minNum到maxNum的随机数
    randomNum: function(minNum, maxNum) {
      switch (arguments.length) {
        case 1:
          return parseInt(Math.random() * minNum + 1, 10)
        case 2:
          return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
        default:
          return 0
      }
    },
    // 检查食物是否生成在身体的位置
    checkInSideBody: function(body, food) {
      let flag = false
      for (let i = 0; i < body.length; i++) {
        const xy = body[i]
        if (xy[0] === food[0] && xy[1] === food[1]) {
          flag = true
          break
        }
      }
      return flag
    },
    stop: function() {
      if (this.interval) {
        clearInterval(this.interval)
      }
    }
  }
}
</script>
